package contact

import (
	"bytes"
	"fmt"
	"github.com/jinzhu/gorm"
	"goim-pro/internal/app/models"
	"goim-pro/internal/db"
	"goim-pro/pkg/logs"
	"goim-pro/pkg/utils"
	"time"
)

type ContactImpl struct {
	models.Contact
	db *gorm.DB
}

type IContactRepo interface {
	IsContactExists(userId, contactId string) (isExist bool, err error)
	FindOne(condition map[string]interface{}) (contact *models.Contact, err error)
	FindAll(condition interface{}) (contacts []models.Contact, err error)
	InsertContacts(newContacts ...*models.Contact) (err error)
	RemoveContactsByIds(userId string, contactIds ...string) (err error)
	FindOneAndUpdateRemark(condition interface{}, remarkInfo interface{}) (contact *models.Contact, err error)
	FuzzySearch(userId string, key string) (contacts []models.Contact, err error)
}

var logger = logs.GetLogger("ERROR")

func NewContactRepo(db *gorm.DB) IContactRepo {
	return &ContactImpl{
		db: db,
	}
}

func (c *ContactImpl) IsContactExists(userId, contactId string) (isExists bool, err error) {
	db := c.db.First(&models.Contact{}, "userId = ? and contactId = ?", userId, contactId)
	if db.RecordNotFound() {
		return false, nil
	}
	if err = db.Error; err != nil {
		logger.Errorf("checking contact error: %v", err)
		return false, err
	}
	return true, nil
}

func (c *ContactImpl) FindOne(condition map[string]interface{}) (contact *models.Contact, err error) {
	contact = &models.Contact{}
	db := c.db.Preload("User").First(contact, condition)
	if db.RecordNotFound() {
		return nil, nil
	}
	if err = db.Error; err != nil {
		logger.Errorf("error happened to query user information: %v", err)
	}
	return
}

func (c *ContactImpl) FindAll(condition interface{}) (contacts []models.Contact, err error) {
	// SELECT * FROM `contacts`  WHERE `contacts`.`deletedAt` IS NULL AND ((`contacts`.`UserId` = '01E07SG858N3CGV5M1APVQKZYR'))
	// SELECT * FROM `users`  WHERE `users`.`deletedAt` IS NULL AND ((`userId` IN ('01E2JVWZTG60NG2SXFYNEPNMCB','01E2JXMC98SZXMGEGVTDECSD78')))
	db := c.db.Preload("User").Where(condition).Find(&contacts)
	if db.RecordNotFound() {
		return nil, nil
	}
	if err = db.Error; err != nil {
		return nil, err
	}
	return contacts, nil
}

func (c *ContactImpl) InsertContacts(newContacts ...*models.Contact) (err error) {
	// BatchSave 批量插入数据
	var buffer bytes.Buffer
	sql := "INSERT INTO `contacts` (`userId`, `contactId`, `remarkName`, `telephone`, `description`, `tags`, `createdAt`, `updatedAt`) values"
	if _, err := buffer.WriteString(sql); err != nil {
		return err
	}

	for i, e := range newContacts {
		nowDateTime := utils.TimeFormat(time.Now(), utils.MysqlDateTimeFormat)

		if i == len(newContacts)-1 {
			buffer.WriteString(fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');", e.UserId, e.ContactId, e.RemarkName, e.Telephone, e.Description, e.Tags, nowDateTime, nowDateTime))
		} else {
			buffer.WriteString(fmt.Sprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'),", e.UserId, e.ContactId, e.RemarkName, e.Telephone, e.Description, e.Tags, nowDateTime, nowDateTime))
		}
	}
	if err = c.db.Exec(buffer.String()).Error; err != nil {
		logger.Errorf("exec insert contacts error: %v", err)
	}
	return
}

// remove user's contacts by contact ids, by force
func (c *ContactImpl) RemoveContactsByIds(userId string, contactIds ...string) (err error) {
	_db := c.db.Unscoped().Delete(&models.Contact{}, "userId = ? and contactId IN (?)", userId, contactIds)
	if _db.Error != nil {
		logger.Errorf("error happened to remove user: %v", _db.Error)
		err = _db.Error
	}
	return
}

func (c *ContactImpl) FindOneAndUpdateRemark(condition interface{}, remarkProfile interface{}) (contact *models.Contact, err error) {
	contact = new(models.Contact)
	db := c.db.Table(tbl.TableContacts).Where(condition).Update(remarkProfile).First(contact)
	if err = db.Error; err != nil {
		logger.Errorf("error happened to update user profile: %v", err)
		return nil, err
	}
	return contact, nil
}

func (c *ContactImpl) FuzzySearch(userId string, key string) (contacts []models.Contact, err error) {
	key = "%" + key + "%"
	db := c.db.Preload("User").Where("(userId = ?) and (remarkName LIKE ? or telephone LIKE ?)", userId, key, key).Find(&contacts)
	if db.RecordNotFound() {
		return nil, nil
	}
	if err = db.Error; err != nil {
		return nil, err
	}
	return contacts, nil
}
